package com.qingmeng.apiclientsdk.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.qingmeng.apiclientsdk.client.ApiClient;
import com.qingmeng.apiclientsdk.exception.ApiException;
import com.qingmeng.apiclientsdk.exception.ErrorCode;
import com.qingmeng.apiclientsdk.exception.ErrorResponse;
import com.qingmeng.apiclientsdk.model.Request.BaseRequest;
import com.qingmeng.apiclientsdk.model.Response.ResultResponse;
import com.qingmeng.apiclientsdk.service.ApiService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import static com.qingmeng.apiclientsdk.utils.SignUtils.getSign;

/**
 * @Author: WPF
 * @DateTime: 2025/01/02 17:57
 * @Version: 1.0
 * @Description:
 */

@Data
@Slf4j
public abstract class BaseServiceImpl implements ApiService {

    private ApiClient apiClient;


    /**
     * 网关HOST
     */
    private String gatewayHost = "http://localhost:8090";

    /**
     * 通用请求（自定义客户端）
     * @param apiClient api客户端
     * @param request 请求对象
     * @return T
     */
    @Override
    public <O, T extends ResultResponse> T request(ApiClient apiClient, BaseRequest<O, T> request) throws ApiException {
        // 1、 检查客户端配置
        checkClientConfig(apiClient);
        // 2、调用请求
        return request(request);
    }


    /**
     * 通用请求
     * @param request 请求对象
     * @return T
     */
    @Override
    public <O, T extends ResultResponse> T request(BaseRequest<O, T> request) throws ApiException {
        try {
            return getResponseData(request);
        } catch (Exception e) {
            throw new ApiException(ErrorCode.OPERATION_ERROR, e.getMessage());
        }
    }

    /**
     * 发送请求并获取响应数据
     * @param request 请求对象
     * @return T
     */
    private <O,T extends ResultResponse> T getResponseData(BaseRequest<O, T> request) throws ApiException, InstantiationException, IllegalAccessException {
        // 1、校验客户端是否存在
        if (apiClient == null || StringUtils.isAnyBlank(apiClient.getAccessKey(), apiClient.getSecretKey())){
            throw new ApiException(ErrorCode.NO_AUTH_ERROR, "请先配置密钥AccessKey/SecretKey");
        }
       // 2、定义一个泛型变量并获取其类型
        T res = getResponseType(request);

//        T res;
//        try {
//            // 3、获取请求的响应类型(抽象方法，由子类实现)
//            Class<T> responseClass = request.getResponseClass();
//            //将response的类型设置为该类型
//            res = responseClass.newInstance();
//        }catch (Exception e){
//            throw new ApiException(ErrorCode.OPERATION_ERROR, "响应类型转换异常");
//        }
        // 4、执行请求
        HttpResponse httpResponse = executeRequest(request);
        // 5、获取响应数据
        String body = httpResponse.body();
        // 6、定义一个MAP集合，用来存储响应（无论请求成功与否，都需要响应对应信息）
        Map<String, Object> data = new HashMap<>();
        // 7、判断响应状态码
        if (httpResponse.getStatus() != 200){
            // 8、请求失败，返回统一的错误对象
            ErrorResponse bean = JSONUtil.toBean(body, ErrorResponse.class);
            data.put("code",bean.getCode());
            data.put("message", bean.getMessage());
        }else {
            // 9、请求成功，解析响应数据
            try {
                // 尝试解析为JSON对象
                data = new Gson().fromJson(body, new TypeToken<Map<String, Object>>() {
                }.getType());
            }catch (Exception e){
                // 解析失败，将body作为普通字符串处理
                data.put("value", body);
            }
        }
        // 10、返回响应数据（因为已经设置rsp的类型，所以它有一些属性）
        res.setData(data);
        return res;
    }

    /**
     * 获取响应数据类型
     * @param request
     * @return
     * @param <O>
     * @param <T>
     * @throws ApiException
     */
    private static <O, T extends ResultResponse> T getResponseType(BaseRequest<O, T> request) throws ApiException {
        T res;
        try {
            // 3、获取请求的响应类型(抽象方法，由子类实现)
            Class<T> responseClass = request.getResponseClass();

            // 使用 getDeclaredConstructor().newInstance() 替代已弃用的 newInstance()
            Constructor<T> constructor = responseClass.getDeclaredConstructor();
            constructor.setAccessible(true);  // 确保私有构造函数也可以访问

            res = constructor.newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new ApiException(ErrorCode.OPERATION_ERROR, "响应类型转换异常: " + e.getMessage());
        }
        return res;
    }

    /**
     * 执行请求
     * @param request 请求对象
     * @return Httpresponse
     */
    private <O, T extends ResultResponse> HttpResponse executeRequest(BaseRequest<O,T> request) throws ApiException {
        // 1、根据请求方法获取HTTP响应类型
        // 2、执行请求，成功返回响应结果，失败则抛出异常(.executr()方法就是执行)
        try {
            return getHttpRequestByRequestMethod(request).execute();
        }catch (Exception e){
            throw new ApiException(ErrorCode.OPERATION_ERROR, "请求异常");
        }
    }

    /**
     * 根据请求方法获取HTTP响应类型
     * @param request 请求对象
     * @return String
     */
    private <O, T extends ResultResponse> HttpRequest getHttpRequestByRequestMethod(BaseRequest<O,T> request) throws ApiException {
        // 1、校验请求参数
        if (ObjectUtils.isEmpty(request.getMethod()) || ObjectUtils.isEmpty(request.getPath())) {
            throw new ApiException(ErrorCode.OPERATION_ERROR, "请求参数错误");
        }
        // 2、格式化请求路径和请求方法
        String path = request.getPath().trim();
        String method = request.getMethod().trim().toUpperCase();
        // 3、如果请求路径以网关HOST开头，则去掉网关HOST
        if (path.startsWith(gatewayHost)){
           path = path.substring(gatewayHost.length());
        }
        log.info("请求路径：{}，请求方法：{}，请求参数：{}", path, method, request.getRequestParams());
        // 4、根据请求方法创建HttpRequest对象
        HttpRequest httpRequest;
        switch (method) {
            case "GET":
                // 5、如果是Get请求需要将请求参数拼接到请求路径中
                httpRequest = HttpRequest.get(splicingGetRequest(request,path));
                break;
            case "POST":
                httpRequest = HttpRequest.post(this.gatewayHost + path);
                break;
            default:
                throw new ApiException(ErrorCode.OPERATION_ERROR, "不支持的请求方法");
        }
        // 6、设置请求头和请求体
        return httpRequest.addHeaders(getHeaders(JSONUtil.toJsonStr(request),apiClient))
                .body(JSONUtil.toJsonStr(request.getRequestParams()));
    }

    /**
     * 拼接Get请求
     *
     * @param request 请求对象
     * @param path    路径
     * @return {@link String}
     */
    private <O, T extends ResultResponse> String splicingGetRequest(BaseRequest<O, T> request, String path) {
        // 1、创建一个StringBuilder对象，用于拼接请求路径
        StringBuilder urlBuilder = new StringBuilder(gatewayHost);

        // 2、如果urlBuilder是以/结尾且path以/开头的情况下，去掉urlBuilder结尾的/
        if (urlBuilder.toString().endsWith("/") && path.startsWith("/")) {
            urlBuilder.setLength(urlBuilder.length() - 1);
        }
        // 3、将请求路径拼接到网关路径后面
        urlBuilder.append(path);
        // 4、如果请求参数不为空，则将请求参数拼接到请求路径后面
        if (!request.getRequestParams().isEmpty()) {
            urlBuilder.append("?");
            for (Map.Entry<String, Object> entry : request.getRequestParams().entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue().toString();
                urlBuilder.append(key).append("=").append(value).append("&");
            }
            urlBuilder.deleteCharAt(urlBuilder.length() - 1);// 5、去掉最后面的"&"
        }
        log.info("GET请求路径：{}", urlBuilder);
        // 6、返回拼接好的新的请求路径
        return urlBuilder.toString();
    }


    /**
     *  获取请求头
     *  /day/wrapper
     * @param body 请求体
     * @param apiClient API客户端
     * @return Map请求头
     */
    private Map<String , String> getHeaders(String body,ApiClient apiClient){
        Map<String, String> headerMap = new HashMap<>();
        headerMap.put("accessKey", apiClient.getAccessKey());// 设置AK
        String encodedBody = SecureUtil.md5(body);// 对请求体进行MD5加密
        headerMap.put("body", encodedBody);// 设置加密后的请求体
        headerMap.put("nonce", RandomUtil.randomNumbers(4));// 设置nonce 随机数
        headerMap.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));// 设置时间戳
        headerMap.put("sign", getSign(encodedBody, apiClient.getSecretKey()));// 设置签名sign
        return headerMap;
    }

    /**
     * 检查客户端配置
     * @param apiClient API客户端
     */
    private void checkClientConfig(ApiClient apiClient) throws ApiException {
        if (apiClient == null && this.getApiClient() == null) {
            throw new ApiException(ErrorCode.NO_AUTH_ERROR, "请先配置密钥AccessKey/SecretKey");
        }
        if (apiClient != null && !StringUtils.isAnyBlank(apiClient.getAccessKey(), apiClient.getSecretKey())) {
            this.setApiClient(apiClient);
        }
    }


}
